#include "relay_app.h"

#include "esp_log.h"
#include <cstring>

#include "mqtt_manager.h"
#include "system_info.h"
#include "board.h"
#include "esp_task_manager.h"
#include "logger_app.h"

#define TAG "RELAY_APP"

RelayApp::RelayApp(){
    ESP_LOGI(TAG, "继电器应用初始化");
    task_executor_ = TaskManager::GetInstance().CreateExecutor("relay_app", 4096, 4, 0);
    // 初始化Settings实例，用于保存继电器状态
    settings_ = std::make_unique<Settings>("relay_app", true); // 读写模式
}

bool RelayApp::start(){
    pcf8574_array_ = Board::GetInstance().GetPCF8574Array();
    sprintf(get_topic,"/c/%s/get",SystemInfo::GetMacAddress().c_str());
    sprintf(send_topic,"/c/%s/send",SystemInfo::GetMacAddress().c_str());
    if(!MqttManager::GetInstance().SubscribeRealtime(get_topic, 1, CallBack, this)){
        ESP_LOGE(TAG, "启动失败: 订阅主题失败 %s", get_topic);
        return false;
    }
    
    // 恢复保存的继电器状态
    RestoreRelayStates();
    
    ESP_LOGI(TAG, "继电器应用启动成功");
    return true;
}

void RelayApp::CallBack(const char* topic, const char* payload, size_t length, void* user_data) {
    auto* relay_app = static_cast<RelayApp*>(user_data);
    
    if (!relay_app) {
        ESP_LOGE(TAG, "用户数据为空，无法处理继电器消息");
        return;
    }
    
    // 将char*转换为uint8_t*来处理二进制数据
    const uint8_t* binary_payload = reinterpret_cast<const uint8_t*>(payload);
    
    relay_app->ProcessProtocolMessage(topic, binary_payload, length);
}

void RelayApp::ProcessProtocolMessage(const char* topic, const uint8_t* payload, size_t length) {
    // 检查是否为8字节协议格式
    if (length != 8) {
        ESP_LOGW(TAG, "数据长度不正确，期望8字节，实际%zu字节", length);
        return;
    }
    
    // 检查协议头
    if (payload[0] != 0x55) {
        ESP_LOGW(TAG, "协议头错误，期望0x55，实际0x%02X", payload[0]);
        return;
    }
    
    uint8_t address = payload[1];
    uint8_t function_code = payload[2];
    uint8_t data1 = payload[3];
    uint8_t data2 = payload[4];
    uint8_t data3 = payload[5];
    uint8_t data4 = payload[6];
    
    // 处理不同的功能码
    switch (function_code) {
        case 0x10: // 读取继电器状态
            ESP_LOGI(TAG, "读取继电器状态");
            ReturnRelayState();
            break;
            
        case 0x12: // 打开继电器
            ESP_LOGI(TAG, "打开继电器 %d", data4);
            RelayOpen(data4);
            SaveRelayStates();
            ReturnRelayState();
            break;
            
        case 0x11: // 关闭继电器
            ESP_LOGI(TAG, "关闭继电器 %d", data4);
            RelayClose(data4);
            SaveRelayStates();
            ReturnRelayState();
            break;
            
        case 0x21: // 打开->延时->关闭
            {
                uint32_t delay_ms = (data1 << 16) | (data2 << 8) | data3;
                ESP_LOGI(TAG, "继电器%d 延时控制: 打开%ldms后关闭", data4, delay_ms);
                RelayOpenDelay(data4, delay_ms);
                ReturnRelayState();
                break;
            }
            
        case 0x22: // 关闭->延时->打开
            {
                uint32_t delay_ms = (data1 << 16) | (data2 << 8) | data3;
                ESP_LOGI(TAG, "继电器%d 延时控制: 关闭%ldms后打开", data4, delay_ms);
                RelayCloseDelay(data4, delay_ms);
                ReturnRelayState();
                break;
            }
            
        case 0x13: // 控制全部继电器
            if (data4 == 0xFF) {
                ESP_LOGI(TAG, "打开所有继电器");
                AllOpen();
            } else if (data4 == 0x00) {
                ESP_LOGI(TAG, "关闭所有继电器");
                AllClose();
            }
            SaveRelayStates();
            ReturnRelayState();
            break;
            
        case 0x81: // 重启系统
            ESP_LOGI(TAG, "系统重启");
            esp_restart();
            break;
            
        default:
            ESP_LOGW(TAG, "未知功能码: 0x%02X", function_code);
            break;
    }
}

uint8_t RelayApp::GetChecksum(const uint8_t* data, uint8_t len) {
    uint8_t sum = 0;
    for (uint8_t i = 0; i < len; i++) {
        sum += data[i];
    }
    return sum;
}

void RelayApp::ReturnRelayState() {
    // 设置回复数据包
      //relay_state_ 数组中，第16-9路继电器状态存储在relay_state_[0]，第8-1路继电器状态存储在relay_state_[1]
    //  0  0  0  0  0  0  0  0   0  0  0  0  0  0  0  0
    // [   relay_state_[0]   ]  [    relay_state_[1]  ]
    // 16 15 14 13 12 11 10 09  08 07 06 05 04 03 02 01
    data_back_[2] = 0x10; // 功能码
    data_back_[3] = 0x00; // 数据1
    data_back_[4] = 0x00; // 数据2
    data_back_[5] = relay_state_[0]; // 第16-9路继电器状态
    data_back_[6] = relay_state_[1]; // 第8-1路继电器状态  
    data_back_[7] = GetChecksum(data_back_, 7); // 校验和
    
    PublishMqttMessage(data_back_, 8);
}

void RelayApp::RelayOpen(uint8_t relay_num) {
    if (relay_num < 1 || relay_num > 14) {
        ESP_LOGE(TAG, "无效继电器编号: %d (支持1-14)", relay_num);
        LoggerApp::GetInstance().RecordRelayError();
        return;
    }
    
    UpdatePCF8574Output(relay_num, 1);
    ESP_LOGI(TAG, "继电器 %d 已打开", relay_num);
}

void RelayApp::RelayClose(uint8_t relay_num) {
    if (relay_num < 1 || relay_num > 14) {
        ESP_LOGE(TAG, "无效继电器编号: %d (支持1-14)", relay_num);
        LoggerApp::GetInstance().RecordRelayError();
        return;
    }
    
    UpdatePCF8574Output(relay_num, 0);
    ESP_LOGI(TAG, "继电器 %d 已关闭", relay_num);
}

void RelayApp::AllOpen() {
    for (uint8_t i = 1; i <= 14; i++) {
        UpdatePCF8574Output(i, 1);
    }
    ESP_LOGI(TAG, "所有继电器已打开");
}

void RelayApp::AllClose() {
    for (uint8_t i = 1; i <= 14; i++) {
        UpdatePCF8574Output(i, 0);
    }
    ESP_LOGI(TAG, "所有继电器已关闭");
}

void RelayApp::RelayOpenDelay(uint8_t relay_num, uint32_t delay_ms) {
    if (relay_num < 1 || relay_num > 14) {
        ESP_LOGE(TAG, "无效继电器编号: %d (支持1-14)", relay_num);
        LoggerApp::GetInstance().RecordRelayError();
        return;
    }
    
    task_executor_->AddTask([this, relay_num, delay_ms]() {
        // 先打开继电器
        this->RelayOpen(relay_num);
        
        // 延时
        vTaskDelay(pdMS_TO_TICKS(delay_ms));
        
        // 然后关闭继电器
        this->RelayClose(relay_num);
        ESP_LOGI(TAG, "延时任务完成: 继电器 %d", relay_num);
        // 保存继电器状态到NVS
        SaveRelayStates();
    }, TaskType::IMMEDIATE, TaskPriority::TASKLOW, "relay_open_delay");
}

void RelayApp::RelayCloseDelay(uint8_t relay_num, uint32_t delay_ms) {
    if (relay_num < 1 || relay_num > 14) {
        ESP_LOGE(TAG, "无效继电器编号: %d (支持1-14)", relay_num);
        LoggerApp::GetInstance().RecordRelayError();
        return;
    }
    
    task_executor_->AddTask([this, relay_num, delay_ms]() {
        // 先关闭继电器
        this->RelayClose(relay_num);
        
        // 延时
        vTaskDelay(pdMS_TO_TICKS(delay_ms));
        
        // 然后打开继电器
        this->RelayOpen(relay_num);
        ESP_LOGI(TAG, "延时任务完成: 继电器 %d", relay_num);
        // 保存继电器状态到NVS
        SaveRelayStates();
    }, TaskType::IMMEDIATE, TaskPriority::TASKLOW, "relay_close_delay");
}

void RelayApp::UpdatePCF8574Output(uint8_t relay_num, uint8_t state) {
    if (relay_num < 1 || relay_num > 14) {
        ESP_LOGE(TAG, "无效继电器编号: %d (支持1-14)", relay_num);
        LoggerApp::GetInstance().RecordRelayError();
        return;
    }
    
    // PCF8574设备映射：L1=pcf8574_array_[1], L2=pcf8574_array_[0], L3=pcf8574_array_[2]
    PCF8574* device = nullptr;
    uint8_t pin1 = 0, pin2 = 0;
    bool is_dual_pin = true; // 是否为双引脚控制
    uint8_t state_byte_index = 0;
    uint8_t state_bit_mask = 0;
    
    switch (relay_num) {
        case 1:  // L2 引脚6,7
            device = pcf8574_array_[0]; 
            pin1 = 6; pin2 = 7;
            state_byte_index = 1; state_bit_mask = 0x01;
            break;
        case 2:  // L2 引脚4,5
            device = pcf8574_array_[0];
            pin1 = 4; pin2 = 5;
            state_byte_index = 1; state_bit_mask = 0x02;
            break;
        case 3:  // L1 引脚6,7
            device = pcf8574_array_[1];
            pin1 = 6; pin2 = 7;
            state_byte_index = 1; state_bit_mask = 0x04;
            break;
        case 4:  // L1 引脚4,5
            device = pcf8574_array_[1];
            pin1 = 4; pin2 = 5;
            state_byte_index = 1; state_bit_mask = 0x08;
            break;
        case 5:  // L3 引脚6,7
            device = pcf8574_array_[2];
            pin1 = 6; pin2 = 7;
            state_byte_index = 1; state_bit_mask = 0x10;
            break;
        case 6:  // L3 引脚4,5
            device = pcf8574_array_[2];
            pin1 = 4; pin2 = 5;
            state_byte_index = 1; state_bit_mask = 0x20;
            break;
        case 7:  // L3 引脚3,2
            device = pcf8574_array_[2];
            pin1 = 3; pin2 = 2;
            state_byte_index = 1; state_bit_mask = 0x40;
            break;
        case 8:  // L3 引脚0,1
            device = pcf8574_array_[2];
            pin1 = 0; pin2 = 1;
            state_byte_index = 1; state_bit_mask = 0x80;
            break;
        case 9:  // L1 引脚3,2
            device = pcf8574_array_[1];
            pin1 = 3; pin2 = 2;
            state_byte_index = 0; state_bit_mask = 0x01;
            break;
        case 10: // L1 引脚0,1
            device = pcf8574_array_[1];
            pin1 = 0; pin2 = 1;
            state_byte_index = 0; state_bit_mask = 0x02;
            break;
        case 11: // L2 引脚0 (单引脚)
            device = pcf8574_array_[0];
            pin1 = 0;
            is_dual_pin = false;
            state_byte_index = 0; state_bit_mask = 0x04;
            break;
        case 12: // L2 引脚1 (单引脚)
            device = pcf8574_array_[0];
            pin1 = 1;
            is_dual_pin = false;
            state_byte_index = 0; state_bit_mask = 0x08;
            break;
        case 13: // L2 引脚2 (单引脚)
            device = pcf8574_array_[0];
            pin1 = 2;
            is_dual_pin = false;
            state_byte_index = 0; state_bit_mask = 0x10;
            break;
        case 14: // L2 引脚3 (单引脚)
            device = pcf8574_array_[0];
            pin1 = 3;
            is_dual_pin = false;
            state_byte_index = 0; state_bit_mask = 0x20;
            break;
        default:
            ESP_LOGE(TAG, "未支持的继电器编号: %d", relay_num);
            return;
    }
    
    if (!device) {
        ESP_LOGE(TAG, "PCF8574设备无效，继电器%d", relay_num);
        LoggerApp::GetInstance().RecordRelayError();
        return;
    }
    
    // 更新内部状态
    if (state) {
        relay_state_[state_byte_index] |= state_bit_mask;
    } else {
        relay_state_[state_byte_index] &= ~state_bit_mask;
    }
    
    // 控制继电器
    if (is_dual_pin) {
        // 双引脚继电器控制
        if (state) {
            // 打开继电器：pin1=HIGH, pin2=LOW
            device->digitalWrite(pin1, HIGH);
            device->digitalWrite(pin2, LOW);
        } else {
            // 关闭继电器：pin1=LOW, pin2=HIGH
            device->digitalWrite(pin1, LOW);
            device->digitalWrite(pin2, HIGH);
        }
        
        // 延时200ms
        vTaskDelay(pdMS_TO_TICKS(200));
        
        // 两个引脚都设为LOW
        device->digitalWrite(pin1, LOW);
        device->digitalWrite(pin2, LOW);
        
    } else {
        // 单引脚继电器控制
        device->digitalWrite(pin1, state ? HIGH : LOW);
        
        // 延时200ms
        vTaskDelay(pdMS_TO_TICKS(200));
    }
    
    // 最后延时200ms
    vTaskDelay(pdMS_TO_TICKS(200));
}

void RelayApp::SaveRelayStates() {
    if (!settings_) {
        ESP_LOGW(TAG, "Settings未初始化，无法保存继电器状态");
        return;
    }
    
    // 先读取NVS中当前存储的值
    uint8_t current_nvs_state_0 = settings_->GetUint8("relay_state_0", 0xFF);
    uint8_t current_nvs_state_1 = settings_->GetUint8("relay_state_1", 0xFF);
    
    // 检查是否需要更新
    bool need_update_0 = (current_nvs_state_0 != relay_state_[0]);
    bool need_update_1 = (current_nvs_state_1 != relay_state_[1]);
    
    if (!need_update_0 && !need_update_1) {
        return;
    }
    
    // 只更新发生变化的字节
    if (need_update_0) {
        settings_->SetUint8("relay_state_0", relay_state_[0]);
        LoggerApp::GetInstance().RecordNvsWrite();
    }
    
    if (need_update_1) {
        settings_->SetUint8("relay_state_1", relay_state_[1]);
        LoggerApp::GetInstance().RecordNvsWrite();
    }
    
    ESP_LOGI(TAG, "继电器状态已保存: [0x%02X, 0x%02X]", 
             relay_state_[0], relay_state_[1]);
}

void RelayApp::RestoreRelayStates() {
    if (!settings_) {
        ESP_LOGW(TAG, "Settings未初始化，无法恢复继电器状态");
        return;
    }
    
    // 从NVS读取保存的继电器状态
    relay_state_[0] = settings_->GetUint8("relay_state_0", 0);
    relay_state_[1] = settings_->GetUint8("relay_state_1", 0);
    
    ESP_LOGI(TAG, "恢复继电器状态: [0x%02X, 0x%02X]", 
             relay_state_[0], relay_state_[1]);
    
    // 恢复每个继电器的物理状态
    for (uint8_t relay_num = 1; relay_num <= 14; relay_num++) {
        uint8_t state_byte_index = 0;
        uint8_t state_bit_mask = 0;
        
        // 确定状态字节索引和位掩码
        switch (relay_num) {
            case 1: state_byte_index = 1; state_bit_mask = 0x01; break;
            case 2: state_byte_index = 1; state_bit_mask = 0x02; break;
            case 3: state_byte_index = 1; state_bit_mask = 0x04; break;
            case 4: state_byte_index = 1; state_bit_mask = 0x08; break;
            case 5: state_byte_index = 1; state_bit_mask = 0x10; break;
            case 6: state_byte_index = 1; state_bit_mask = 0x20; break;
            case 7: state_byte_index = 1; state_bit_mask = 0x40; break;
            case 8: state_byte_index = 1; state_bit_mask = 0x80; break;
            case 9: state_byte_index = 0; state_bit_mask = 0x01; break;
            case 10: state_byte_index = 0; state_bit_mask = 0x02; break;
            case 11: state_byte_index = 0; state_bit_mask = 0x04; break;
            case 12: state_byte_index = 0; state_bit_mask = 0x08; break;
            case 13: state_byte_index = 0; state_bit_mask = 0x10; break;
            case 14: state_byte_index = 0; state_bit_mask = 0x20; break;
        }
        
        // 检查继电器状态并恢复
        uint8_t state = (relay_state_[state_byte_index] & state_bit_mask) ? 1 : 0;
        RestoreRelayPhysicalState(relay_num, state);
    }
    
    ESP_LOGI(TAG, "继电器状态恢复完成");
    
    // 恢复完成后发布当前继电器状态
    ReturnRelayState();
}

void RelayApp::RestoreRelayPhysicalState(uint8_t relay_num, uint8_t state) {
    if (relay_num < 1 || relay_num > 14) {
        ESP_LOGE(TAG, "无效继电器编号: %d (支持1-14)", relay_num);
        LoggerApp::GetInstance().RecordRelayError();
        return;
    }
    
    // 获取PCF8574设备和引脚映射
    PCF8574* device = nullptr;
    uint8_t pin1 = 0, pin2 = 0;
    bool is_dual_pin = true;
    
    switch (relay_num) {
        case 1:  device = pcf8574_array_[0]; pin1 = 6; pin2 = 7; break;
        case 2:  device = pcf8574_array_[0]; pin1 = 4; pin2 = 5; break;
        case 3:  device = pcf8574_array_[1]; pin1 = 6; pin2 = 7; break;
        case 4:  device = pcf8574_array_[1]; pin1 = 4; pin2 = 5; break;
        case 5:  device = pcf8574_array_[2]; pin1 = 6; pin2 = 7; break;
        case 6:  device = pcf8574_array_[2]; pin1 = 4; pin2 = 5; break;
        case 7:  device = pcf8574_array_[2]; pin1 = 3; pin2 = 2; break;
        case 8:  device = pcf8574_array_[2]; pin1 = 0; pin2 = 1; break;
        case 9:  device = pcf8574_array_[1]; pin1 = 3; pin2 = 2; break;
        case 10: device = pcf8574_array_[1]; pin1 = 0; pin2 = 1; break;
        case 11: device = pcf8574_array_[0]; pin1 = 0; is_dual_pin = false; break;
        case 12: device = pcf8574_array_[0]; pin1 = 1; is_dual_pin = false; break;
        case 13: device = pcf8574_array_[0]; pin1 = 2; is_dual_pin = false; break;
        case 14: device = pcf8574_array_[0]; pin1 = 3; is_dual_pin = false; break;
        default: return;
    }
    
    if (!device) {
        ESP_LOGE(TAG, "PCF8574设备无效，继电器%d", relay_num);
        LoggerApp::GetInstance().RecordRelayError();
        return;
    }
    
    // 恢复继电器物理状态（仅硬件控制，不保存NVS）
    if (is_dual_pin) {
        // 双引脚继电器控制
        if (state) {
            // 打开继电器：pin1=HIGH, pin2=LOW
            device->digitalWrite(pin1, HIGH);
            device->digitalWrite(pin2, LOW);
            vTaskDelay(pdMS_TO_TICKS(200));
            device->digitalWrite(pin1, LOW);
            device->digitalWrite(pin2, LOW);
        } else {
            // 关闭继电器：pin1=LOW, pin2=HIGH  
            device->digitalWrite(pin1, LOW);
            device->digitalWrite(pin2, HIGH);
            vTaskDelay(pdMS_TO_TICKS(200));
            device->digitalWrite(pin1, LOW);
            device->digitalWrite(pin2, LOW);
        }
    } else {
        // 单引脚继电器控制
        device->digitalWrite(pin1, state ? HIGH : LOW);
        vTaskDelay(pdMS_TO_TICKS(200));
    }
}

void RelayApp::PublishMqttMessage(const uint8_t* data, size_t length) {
    auto& mqtt_manager = MqttManager::GetInstance();
    std::string data_str(reinterpret_cast<const char*>(data), length);
    mqtt_manager.PublishAsync(send_topic, data_str,1);
}


